# -*- coding: utf-8 -*-
"""
Created on Tue Sep 29 14:47:27 2020

@author: Robin
"""
from numpy import *
import numpy as np
import pandas as pd
from scipy import stats
from . import utils
from .variable_template import ICAnalysis,IndicatorAnalysis
import re
from cvxopt import matrix, solvers

#from lib import utils
#from lib.variable_template import *

# In[0]: 原有函数

def mean_return_by_quantile(factor_data,
                            by_date=False,
                            by_group=False,
                            demeaned=True,
                            group_adjust=False):
    """
    Computes mean returns for factor quantiles across
    provided forward returns columns.

    Parameters
    ----------
    factor_data : pd.DataFrame - MultiIndex
        A MultiIndex DataFrame indexed by date (level 0) and asset (level 1),
        containing the values for a single alpha factor, forward returns for
        each period, the factor quantile/bin that factor value belongs to, and
        (optionally) the group the asset belongs to.
        - See full explanation in utils.get_clean_factor_and_forward_returns
    by_date : bool
        If True, compute quantile bucket returns separately for each date.
    by_group : bool
        If True, compute quantile bucket returns separately for each group.
    demeaned : bool
        Compute demeaned mean returns (long short portfolio)
    group_adjust : bool
        Returns demeaning will occur on the group level.

    Returns
    -------
    mean_ret : pd.DataFrame
        Mean period wise returns by specified factor quantile.
    std_error_ret : pd.DataFrame
        Standard error of returns by specified quantile.
    """

    if group_adjust:
        grouper = [factor_data.index.get_level_values('date')] + ['group']
        factor_data = utils.demean_forward_returns(factor_data, grouper)
    elif demeaned:
        factor_data = utils.demean_forward_returns(factor_data)
    else:
        factor_data = factor_data.copy()

    grouper = ['factor_quantile', factor_data.index.get_level_values('date')]

    if by_group:
        grouper.append('group')

    group_stats = factor_data.groupby(grouper)[
        utils.get_forward_returns_columns(factor_data.columns)] \
        .agg(['mean', 'std', 'count'])

    mean_ret = group_stats.T.xs('mean', level=1).T

    if not by_date:
        grouper = [mean_ret.index.get_level_values('factor_quantile')]
        if by_group:
            grouper.append(mean_ret.index.get_level_values('group'))
        group_stats = mean_ret.groupby(grouper)\
            .agg(['mean', 'std', 'count'])
        mean_ret = group_stats.T.xs('mean', level=1).T

    std_error_ret = group_stats.T.xs('std', level=1).T \
        / np.sqrt(group_stats.T.xs('count', level=1).T)

    return mean_ret, std_error_ret

def factor_weights(factor_data,
                   demeaned = True,
                   group_adjust = False,
                   equal_weight = False,
                   group_weight = False):
    """
    Computes asset weights by factor values and dividing by the sum of their
    absolute value (achieving gross leverage of 1). Positive factor values will
    results in positive weights and negative values in negative weights.
    
    // We can pass self-defined weight_data instead of factor_data, so that we can 
    // weight stocks in different methods.

    Parameters
    ----------
    factor_data : pd.DataFrame - MultiIndex
        A MultiIndex DataFrame indexed by date (level 0) and asset (level 1),
        containing the values for a single alpha factor, forward returns for
        each period, the factor quantile/bin that factor value belongs to, and
        (optionally) the group the asset belongs to.
        - See full explanation in utils.get_clean_factor_and_forward_returns
    demeaned : bool
        Should this computation happen on a long short portfolio? if True,
        weights are computed by demeaning factor values and dividing by the sum
        of their absolute value (achieving gross leverage of 1). The sum of
        positive weights will be the same as the negative weights (absolute
        value), suitable for a dollar neutral long-short portfolio
    group_adjust : bool
        Should this computation happen on a group neutral portfolio? If True,
        compute group neutral weights: each group will weight the same and
        if 'demeaned' is enabled the factor values demeaning will occur on the
        group level.
    equal_weight : bool, optional
        if True the assets will be equal-weighted instead of factor-weighted
        If demeaned is True then the factor universe will be split in two
        equal sized groups, top assets with positive weights and bottom assets
        with negative weights
    \\group_weight : dict, optional
    \\    Only when group_adjust is True.

    Returns
    -------
    returns : pd.Series
        Assets weighted by factor value.
    """
    
    def to_weights(group, _demeaned, _equal_weight):

        if _equal_weight:
            group = group.copy()
    
            if _demeaned:
                # top assets positive weights, bottom ones negative
                group = group - group.median()
    
            negative_mask = group < 0
            group[negative_mask] = -1.0
            positive_mask = group > 0
            group[positive_mask] = 1.0
    
            if _demeaned:
                # positive weights must equal negative weights
                if negative_mask.any():
                    group[negative_mask] /= negative_mask.sum()
                if positive_mask.any():
                    group[positive_mask] /= positive_mask.sum()
    
        elif _demeaned:
            group = group - group.mean()
    
        return group / group.abs().sum()
    
    # 分组依据
    grouper = [factor_data.index.get_level_values('date')]
    if group_adjust:
        grouper.append('group')

    # 加权重
    weights = factor_data.groupby(grouper)['factor'] \
        .apply(to_weights, demeaned, equal_weight)
    
    # 组内中性
    if group_adjust:
        weights = weights.groupby(level='date').apply(to_weights, False, False)

    return weights

def factor_information_coefficient(factor_data,
                                   group_adjust=False,
                                   by_group=False):
    """
    Computes the Spearman Rank Correlation based Information Coefficient (IC)
    between factor values and N period forward returns for each period in
    the factor index.

    Parameters
    ----------
    factor_data : pd.DataFrame - MultiIndex
        A MultiIndex DataFrame indexed by date (level 0) and asset (level 1),
        containing the values for a single alpha factor, forward returns for
        each period, the factor quantile/bin that factor value belongs to, and
        (optionally) the group the asset belongs to.
        - See full explanation in utils.get_clean_factor_and_forward_returns
    group_adjust : bool
        Demean forward returns by group before computing IC.
    by_group : bool
        If True, compute period wise IC separately for each group.

    Returns
    -------
    ic : pd.DataFrame
        Spearman Rank correlation between factor and
        provided forward returns.
    """

    def src_ic(group):
        f = group['factor']
        _ic = group[utils.get_forward_returns_columns(factor_data.columns)] \
            .apply(lambda x: stats.spearmanr(x, f)[0])
        return _ic

    factor_data = factor_data.copy()

    grouper = [factor_data.index.get_level_values('date')]

    if group_adjust:
        factor_data = utils.demean_forward_returns(factor_data,
                                                   grouper + ['group'])
    if by_group:
        grouper.append('group')

    ic = factor_data.groupby(grouper).apply(src_ic)

    return ic


#换手率计算（从ROWperformance复制）
def quantile_turnover(quantile_factor, quantile, period=1):
    """
    Computes the proportion of names in a factor quantile that were
    not in that quantile in the previous period.

    Parameters
    ----------
    quantile_factor : pd.Series
        DataFrame with date, asset and factor quantile.
    quantile : int
        Quantile on which to perform turnover analysis.
    period: int, optional
        Number of days over which to calculate the turnover.

    Returns
    -------
    quant_turnover : pd.Series
        Period by period turnover for that quantile.
    """

    quant_names = quantile_factor[quantile_factor == quantile]
    quant_name_sets = quant_names.groupby(level=['date']).apply(
        lambda x: set(x.index.get_level_values('asset')))

    name_shifted = quant_name_sets.shift(period)

    new_names = (quant_name_sets - name_shifted).dropna()
    quant_turnover = new_names.apply(
        lambda x: len(x)) / quant_name_sets.apply(lambda x: len(x))
    quant_turnover.name = quantile
    return quant_turnover

# In[1]: 新增函数
def MaxDrawdown(return_list):
    '''最大回撤率'''
    i = np.argmax((np.maximum.accumulate(return_list) - return_list) / np.maximum.accumulate(return_list))  # 结束位置
    if i == 0:
        return 0
    j = np.argmax(return_list[:i])  # 开始位置
    return (return_list[j] - return_list[i]) / (return_list[j])


def get_indicator_analysis(factor_data,
                           scale_method = 'normal',
                            ic_method = 'rank',                           
                            ret_method = 'cap',
                            turnover_method = 'count',
                            group_num = 5,                           
                            #ind_direction = 'ascending',
                            industry_neu = False,
                            industry_type = 'sw',                           
                            industry_level = 1):
    """
    传入factor_data，以及相关参数，获取单因子分析结果
    """
    # 1. 分析
    ic_decay = None
    ic_series = factor_information_coefficient(factor_data[{'1D','factor'}],group_adjust=False,by_group=False)
    
    ic_stats = pd.DataFrame()
    ic_stats['IC_mean']=ic_series.mean()
    ic_stats['IC_std']=ic_series.std()
    ic_stats['IC_IR']=ic_series.mean()/ic_series.std()
        
    # 2. 将分析结果存储到结构体中
    ic_analysis = ICAnalysis(ic_decay,ic_series,ic_stats)
    
    #return 分析
    
    #多空组合平均收益率
    return_analysis = pd.DataFrame()
    
    mean_quant_ret, std_quantile = mean_return_by_quantile(factor_data)
    
    if ic_series.mean().values>0:
        return_analysis['mean return G1-G5']=mean_quant_ret.loc[1]-mean_quant_ret.loc[5]
    
    else:
        return_analysis['mean return G1-G5']=mean_quant_ret.loc[5]-mean_quant_ret.loc[1]
    
    #多空组合最大回撤
    return_G5=factor_data[factor_data['factor_quantile']==5]['1D']
    return_G1=factor_data[factor_data['factor_quantile']==1]['1D']
    
    G5_mean_by_date=return_G5.groupby('date').apply(lambda x:x.mean())
    G1_mean_by_date=return_G1.groupby('date').apply(lambda x:x.mean())
    
    G1_G5_return=G1_mean_by_date-G5_mean_by_date
    
    max_drawdown_G1G5=MaxDrawdown(list(G1_G5_return))
    return_analysis['max drawdown G1-G5']=max_drawdown_G1G5
    
    
    
    #turnover 分析
    #input_periods = utils.get_forward_returns_columns(factor_data.columns, require_exact_day_multiple=True).values
 
    def get_valid_columns(columns):
        pattern = re.compile(r"^(\d+([D]))+$", re.IGNORECASE)
        valid_columns = [(pattern.match(col) is not None) for col in columns]
        return columns[valid_columns]       
    input_periods = get_valid_columns(factor_data.columns)    
    turnover_periods = utils.timedelta_strings_to_integers(input_periods)
    quantile_factor = factor_data["factor_quantile"] 
    
    #quantile_turnover={}
    
    quantile_turnover_dict = {
        p: pd.concat(
            [
                quantile_turnover(quantile_factor, q, p)
                for q in quantile_factor.sort_values().unique().tolist()
            ],
            axis=1,
        )
        for p in turnover_periods
    }    
    
    turnover_analysis=pd.DataFrame(index=list(input_periods),columns=['G1','G2','G3','G4','G5'])
    
    for i in range(len(turnover_periods)):
        for j in range(1,6):
            turnover_analysis.loc[input_periods[i]]['G'+str(j)]=quantile_turnover_dict[turnover_periods[i]].mean()[j]
    
    '''
    turnover_analysis返回的是一个DataFrame，格式如下：
               G1        G2        G3        G4        G5
    1D   0.437037  0.662222  0.672593  0.646667  0.433333
    5D   0.757391  0.791739  0.775217      0.77      0.74
    10D  0.849444  0.818333  0.793889  0.794444  0.841111
    
    显示的是每一组的平均换手率
    '''
    
    
    sector_analysis = None      
    indicator_analysis = IndicatorAnalysis(ic_analysis,return_analysis,sector_analysis,turnover_analysis)
 
    return indicator_analysis


#回测函数，没有权重输入（即等权重）
def back_testing(stock_data,stock_choosed_dict,end_date,period='daily',stock_choosed_type=3):
    '''
    stock_data是股票的收盘价数据，类型为DataFrame，格式如am.stock
                000008.SZ  000009.SZ   000012.SZ  ...  603888.SH   603939.SH  603983.SH
    2019-08-30  77.271640  40.751267  109.844887  ...  54.741059  157.239118  49.910000
    2019-09-06  81.291552  43.320368  113.277540  ...  59.331067  160.195049  48.570000
    2019-09-12  82.408194  43.586137  118.294494  ...  58.574759  154.652678  56.250000
    2019-09-20  88.438062  41.814343  120.142846  ...  58.913794  161.221414  62.100000
    2019-09-27  77.718297  39.865369  112.485389  ...  53.358841  162.986762  60.300000
    2019-09-30  76.601655  39.068062  111.429189  ...  52.028782  161.796179  59.300000
    
    
    stock_choosed_dict是根据日期序列选股的返回结果，是一个字典，键为日期str
    不行业分类时，值为股票名的list
    行业分类时，值为一个dict，键为行业名，值为该行业下的选股list

    end_date是日期str，输入回测结束的日期
    '''
    
    '''
    -----以下一段是把不同的stock_choosed格式转换成一样的-----
    '''
    if stock_choosed_type==3: #不行业分类
        stock_choosed=pd.DataFrame(stock_choosed_dict)
        
    elif stock_choosed_type==4: #行业分类
        date_list=list(stock_choosed_dict.keys())
        stock_choosed=pd.DataFrame(columns=date_list)
        
        industry_list=list(stock_choosed_dict[date_list[0]].keys())
        
        for date in date_list:
            list_stock_choosed=[]
            item=stock_choosed_dict[date]
            for industry in industry_list:
                list_stock_choosed.extend(item[industry].tolist())
            stock_choosed[date]=list_stock_choosed
        
    else:
        pass

    '''
    -----以上代码将stock_choosed_dict转换成了以下格式的DataFrame--------
    stock_choosed
        2020-09-17 2020-09-18 2020-09-21 2020-09-22
    0    000062.SZ  300253.SZ  002217.SZ  300418.SZ
    1    002544.SZ  603712.SH  002281.SZ  002368.SZ
    2    002465.SZ  300010.SZ  002745.SZ  300115.SZ   
    '''
    #回测的日期序列。这个list的内容都是str，形如'20200918'
    date_list=list(stock_choosed.columns)

    ret_history=[]
    
    #对于回测周期中的每一天（除最后一天）操作
    for i in range(len(date_list)-1):
        today=date_list[i]
        tomorrow=date_list[i+1]
        
        position_today=list(stock_choosed[today])
        stock_num=len(position_today)
        
        ret=0
        today_datetime=pd.to_datetime(today)
        tomorrow_datetime=pd.to_datetime(tomorrow)
        
        #对今天持仓的每一只股票，计算其持有到下一期的收益率
        for s in position_today:
            #今天的持仓股票持有到下一期的收益率
            ret_per_stock=(stock_data.loc[tomorrow_datetime][s]-stock_data.loc[today_datetime][s])/stock_data.loc[today_datetime][s]
            ret=ret+ret_per_stock
        
        ret_history.append(ret/stock_num)
        print(today,ret)
    
    #到最后一天了，清仓
    today=date_list[-1]
    today_datetime=pd.to_datetime(today)
    tomorrow_datetime=pd.to_datetime(end_date)
    
    position_today=list(stock_choosed[today])
    ret=0
    for s in position_today:
        ret_per_stock=(stock_data.loc[tomorrow_datetime][s]-stock_data.loc[today_datetime][s])/stock_data.loc[today_datetime][s]
        ret=ret+ret_per_stock
    
    ret_history.append(ret/stock_num)
    
    #计算一些指标
    import empyrical

    ret_series=pd.Series(ret_history,index=date_list)
    annual_return=empyrical.annual_return(ret_series, period=period, annualization=None)
    max_drawdown=empyrical.max_drawdown(ret_series)
    sharpe_ratio=empyrical.sharpe_ratio(ret_series, risk_free=0, period=period, annualization=None)

    df_back_testing=pd.DataFrame()
    df_back_testing['annual_return']=[annual_return]
    df_back_testing['max_drawdown']=[max_drawdown]
    df_back_testing['sharpe_ratio']=[sharpe_ratio]
    
    cum_return=empyrical.cum_returns(ret_series, starting_value=0) 
    profit_history=ret_series.cumsum()

    #画图
    import matplotlib.pyplot as plt
    import matplotlib
    matplotlib.rcParams['axes.unicode_minus'] =False
    
    plt.subplot(111)
    lable_x=np.array(date_list)
    #plt.plot(lable_x, ret_history, color="b", linewidth=1.0, linestyle="-")
    plt.plot(lable_x, cum_return, color="r", linewidth=1.0, linestyle="-")
    plt.ylim(min(cum_return) * 0.9, max(cum_return) * 1.1)
    plt.grid(True)
    plt.show()

    return(df_back_testing)


#带权重的回测
def back_testing_with_weights(stock_data,stock_choosed_dict,end_date,period='daily'):
    '''
    stock_data是股票的收盘价数据，类型为DataFrame，格式如am.stock
                000008.SZ  000009.SZ   000012.SZ  ...  603888.SH   603939.SH  603983.SH
    2019-08-30  77.271640  40.751267  109.844887  ...  54.741059  157.239118  49.910000
    2019-09-06  81.291552  43.320368  113.277540  ...  59.331067  160.195049  48.570000
    2019-09-12  82.408194  43.586137  118.294494  ...  58.574759  154.652678  56.250000
    2019-09-20  88.438062  41.814343  120.142846  ...  58.913794  161.221414  62.100000
    2019-09-27  77.718297  39.865369  112.485389  ...  53.358841  162.986762  60.300000
    2019-09-30  76.601655  39.068062  111.429189  ...  52.028782  161.796179  59.300000
    
    
    stock_choosed_dict是一个字典，键是日期，值也是一个dict
    值的dict键是股票名，值是权重
    *在操作中，stock_choosed_df是self.stock_choosed_with_weight的返回值

    end_date是日期str，输入回测结束的日期
    '''

    #回测的日期序列
    date_list=list(stock_choosed_dict.keys())

    ret_history=[]
    
    #对于回测周期中的每一天（除最后一天）操作
    for i in range(len(date_list)-1):
        today=date_list[i]
        tomorrow=date_list[i+1]
        #这里的today,tomorrow都是datetime的格式
        
        position_today=stock_choosed_dict[today]
        '''
        position_today是一个dict，键是当天的选股股票名，值是权重
        '''
        #all_weights=sum(position_today.values()) #float
        #这是当天全部股票的权重之和，理论上应该是100，实际上总是和100有一点偏差
        
        ret=0        
        #对今天持仓的每一只股票，计算其持有到下一期的收益率
        for s in list(position_today.keys()):
            #今天的持仓股票持有到下一期的收益率
            ret_per_stock=(stock_data.loc[tomorrow][s]-stock_data.loc[today][s])/stock_data.loc[today][s]
            ret=ret+ret_per_stock*position_today[s]
            #收益率乘以这个股票的权重（这里权重的单位是%）
        
        ret_history.append(ret/100) 
        #实际上每天的权重加起来总是和100略有偏差，但方便起见就认为是100了
        print(today,ret)
    
    #到最后一天了，清仓
    today=date_list[-1]
    tomorrow=pd.to_datetime(end_date)
    
    position_today=stock_choosed_dict[today]
    #all_weights=sum(position_today.values()) #float
    
    ret=0
    for s in list(position_today.keys()):
        ret_per_stock=(stock_data.loc[tomorrow][s]-stock_data.loc[today][s])/stock_data.loc[today][s]
        ret=ret+ret_per_stock*position_today[s]
    
    ret_history.append(ret/100)
    
    #计算一些指标
    import empyrical

    ret_series=pd.Series(ret_history,index=date_list)
    annual_return=empyrical.annual_return(ret_series, period=period, annualization=None)
    max_drawdown=empyrical.max_drawdown(ret_series)
    sharpe_ratio=empyrical.sharpe_ratio(ret_series, risk_free=0, period=period, annualization=None)

    df_back_testing=pd.DataFrame()
    df_back_testing['annual_return']=[annual_return]
    df_back_testing['max_drawdown']=[max_drawdown]
    df_back_testing['sharpe_ratio']=[sharpe_ratio]
    
    cum_return=empyrical.cum_returns(ret_series, starting_value=0) 
    profit_history=ret_series.cumsum()

    #画图
    import matplotlib.pyplot as plt
    import matplotlib
    matplotlib.rcParams['axes.unicode_minus'] =False
    
    plt.subplot(111)
    lable_x=np.array(date_list)
    #plt.plot(lable_x, ret_history, color="b", linewidth=1.0, linestyle="-")
    plt.plot(lable_x, cum_return, color="r", linewidth=1.0, linestyle="-")
    plt.ylim(min(cum_return) * 0.9, max(cum_return) * 1.1)
    plt.grid(True)
    plt.show()

    return(df_back_testing)


#寻找最小化跟踪误差的个股权重（人为给定权重上下限）
def minTE(index_ret,stock_ret,high_ratio,low_ratio):
    '''
    index_ret 是一个dataframe，索引是时间DateTime，内容是指数的收益率
    格式如下：
               '000905.SH'
    2015-11-10   0.4137
    2015-11-11   1.4476
    ....         ....
    
    
    stock_ret是一个dataframe，索引是时间DateTime，columns是股票名，内容是各只股票的的收益率
    格式如下：
               '000008.SZ'    '000009.SZ'
    2015-11-10   1.4754        3.5758
    2015-11-11   -0.6462       6.6706
    ....         ....          ....
    
    high_ratio 和 low_ratio 分别是个股在组合中的权重上下限
    
    '''
    
#    L=len(index_ret)  #有L天的数据
    N=len(stock_ret.columns) #有N只股票
    
    P=mat(zeros((N,N)))
    Q=mat(zeros((N,1)))
    

    for date in list(index_ret.index):
        R_t=stock_ret.loc[date].values   #当天各个股票收益率的array,横向量
        R=matrix(R_t)                    #当天各个股票收益率的matrix,纵向量
        r_index=index_ret.loc[date].values[0]   #当天指数收益率
        
        P=P+R*R.T
        Q=Q+r_index*R
    
    Q=Q*(-2)
  
    #把P和Q转换成cvxopt需要的格式（cvxopt.matrix）
    P=matrix(P)
    Q=matrix(Q)

    G=np.vstack((eye(N,N,dtype=float),-eye(N,N,dtype=float)))
    G=matrix(G)

    h=np.vstack((high_ratio*ones((N,1)),-low_ratio*ones((N,1))))
    h=matrix(h)
    
    A=matrix(ones((1,N)))
    b=matrix([1.0])
    
    result = solvers.qp(P,Q,G,h,A,b)

    
    weights=[item for item in result['x']]
    
    return weights

#寻找最小化跟踪误差的个股权重（根据市值比重确定选出的股票权重上下限）
def minTE2(index_ret,stock_ret,market_value):
    '''
    index_ret 是一个dataframe/series，索引是时间DateTime，内容是指数的收益率
    格式如下：
               '000905.SH'
    2015-11-10   0.4137
    2015-11-11   1.4476
    ....         ....
    
    
    stock_ret是一个dataframe，索引是时间DateTime，columns是股票名，内容是各只股票的的收益率
    格式如下：
               '000008.SZ'    '000009.SZ'
    2015-11-10   1.4754        3.5758
    2015-11-11   -0.6462       6.6706
    ....         ....          ....
    
    market_value是一个dataframe，索引是时间DateTime，columns是股票名，内容是各只股票的市值
    格式如下：
               '000008.SZ'    '000009.SZ'
    2015-11-10   
    2015-11-11     

    
    '''
    
    #L=len(index_ret)  #有L天的数据
    N=len(stock_ret.columns) #有N只股票
    
    P=mat(zeros((N,N)))
    Q=mat(zeros((N,1)))
    
    for date in list(stock_ret.index):
        R_t=stock_ret.loc[date].values   #当天各个股票收益率的array,横向量
        R=matrix(R_t)                    #当天各个股票收益率的matrix,纵向量
        r_index=index_ret.loc[date].values[0]   #当天指数收益率
        
        P=P+R*R.T
        Q=Q+r_index*R
    
    Q=Q*(-2)
  
    P=matrix(P)
    Q=matrix(Q)

    G=np.vstack((eye(N,N,dtype=float),-eye(N,N,dtype=float)))
    G=matrix(G)
    
#    h1=1/N*2   #个股权重上限
#    h2=1/N/5   #个股权重下限
    
    today_date=index_ret.index[-1]
    h0=[]
    for stock in list(stock_ret.columns):
        if stock in list(market_value.columns):
            h0.append(market_value.loc[today_date,stock])
        else:
            h0.append(0)
        #h0.append(stock_ret[stock][today_date])
    sum_value=sum(h0)
    h1=h0/sum_value 
    h1=matrix(h1.T)

    h=np.vstack((h1*2,-h1*0.5)) #运行后h为numpy.narray
    h=matrix(h)  #numpy.narray转化为cvxopt.base.matrix
    
    A=matrix(ones((1,N)))
    b=matrix([1.0])
    
    result = solvers.qp(P,Q,G,h,A,b)
    #print('x\n',result['x'])
    
    weights=[item for item in result['x']]
    
    return weights



#def back_testing_2(stock_data,stock_choosed_dict,end_date,period='weekly',stock_choosed_type=3):
#    '''
#    stock_data是股票的收盘价数据，类型为DataFrame，格式如am.stock
#                000008.SZ  000009.SZ   000012.SZ  ...  603888.SH   603939.SH  603983.SH
#    2019-08-30  77.271640  40.751267  109.844887  ...  54.741059  157.239118  49.910000
#    2019-09-06  81.291552  43.320368  113.277540  ...  59.331067  160.195049  48.570000
#    2019-09-12  82.408194  43.586137  118.294494  ...  58.574759  154.652678  56.250000
#    2019-09-20  88.438062  41.814343  120.142846  ...  58.913794  161.221414  62.100000
#    2019-09-27  77.718297  39.865369  112.485389  ...  53.358841  162.986762  60.300000
#    2019-09-30  76.601655  39.068062  111.429189  ...  52.028782  161.796179  59.300000
#    
#    
#    stock_choosed_dict是根据日期序列选股的返回结果，是一个字典，键为日期str
#    不行业分类时，值为股票名的list
#    行业分类时，值为一个dict，键为行业名，值为该行业下的选股list
#
#    end_date是日期str，输入回测结束的日期
#    '''
#    
#    if stock_choosed_type==3: #不行业分类
#        stock_choosed=pd.DataFrame(stock_choosed_dict)
#        
#    elif stock_choosed_type==4:
#        date_list=list(stock_choosed_dict.keys())
#        stock_choosed=pd.DataFrame(columns=date_list)
#        
#        industry_list=list(stock_choosed_dict[date_list[0]].keys())
#        
#        for date in date_list:
#            list_stock_choosed=[]
#            item=stock_choosed_dict[date]
#            for industry in industry_list:
#                list_stock_choosed.extend(item[industry].tolist())
#            stock_choosed[date]=list_stock_choosed
#        
#    else:
#        pass
#    
#    code_list=list(stock_data.columns)  #股票池
#    #回测的日期序列
#    date_list=list(stock_choosed.columns)
#
#    capital=1000000 #初始资金
#        
#    #记录每只股票的买入和卖出时间
#    position=pd.DataFrame(index=code_list,columns=['buy_date','amount','sell_date'])
#    
#    #建立初始买入
#    today=date_list[0]
#    buy_list=list(stock_choosed[today])
#    stock_num=len(buy_list)
#    capital_each=capital/stock_num
#    
#    for stock in buy_list:
#        position.loc[stock]['buy_date']=pd.to_datetime(today)
#        buy_price=stock_data.loc[today][stock]
#        position.loc[stock]['amount']=capital_each/buy_price
#    
    

#在固定的一天-某个行业内-按照股票市值进行加权
def weighted_with_in_industry(stock_choosed_list_in_industry,market_value):
    '''
    stock_choosed_list为list格式，由某一个行业内选出的股票的代码组成:
        
    ['603885.SH','603888.SH','603939.SH']
    
    ----------------------------------------------------------
    
    market_value为series格式:
        
    000008.SZ    7.535955e+09
    603983.SH    2.458932e+10
    
    '''
    
    value=[]
    for stock in stock_choosed_list_in_industry:
        value.append(market_value[stock])
        
    weight=value/sum(value)
    
    a=pd.DataFrame(columns=['asset','weight'])
    a['asset']=stock_choosed_list_in_industry
    a['weight']=weight
    
    return a
        




#对某一天-按照行业分类的选股结果-进行按照市值加权
def weighted_in_oneday_by_value(stock_choosed,industry_weight,market_value):
    
    
    '''
    stock_choosed为dict格式：
    
    {'信息技术': array(['300088.SZ', '300459.SZ', '300316.SZ', '300315.SZ', '300296.SZ'
        '300496.SZ', '002544.SZ', '600718.SH', '600410.SH', '600460.SH'],
       dtype=object),
     
     ......
 
     '金融': array(['002797.SZ', '601099.SH', '600909.SH', '600291.SH', '000750.SZ',
        '601860.SH', '600053.SH'], dtype=object)}
    

     ------------------------------------------------------------
     
     industry_weight为dataframe格式：
     
                  weight
      信息技术    0.01
      公用事业    0.02
      医疗保健    0.04
      可选消费    0.03
      
      ----------------------------------------------------------
      
    market_value为series格式:
        
    000008.SZ    7.535955e+09
    ...
    603983.SH    2.458932e+10

    '''
    a=pd.DataFrame(columns=['asset','weight'])
    for industry in industry_weight.index:
        a=a.append(weighted_with_in_industry(list(stock_choosed[industry]),market_value))
    
    a.set_index('asset')   
    
    return a



